/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package parser;

import gui.MainFrame;
import java.util.HashMap;
import java.util.Stack;

/**

 @author FiLiP
 */
public class SyntaxAnalyzer
{
    private String text;
    
    private HashMap<String, T> map = new HashMap<>();
	
	private MainFrame mainWindow;
    
    private int table[][][] =
    {
        { null, null, null, null, null, null, null, null, null, null, null, null, {N.Element.id}, null, {N.Xmldecl.id, N.Element.id} },
        { null, null, null, null, null, null, null, null, null, null, null, null, null, null, {T.startXmlDecl.id, N.Vernumb.id, T.endXmlDecl.id} },
        { null, null, null, null, null, null, {N.Number.id, T.dot.id, N.Number.id}, null, null, null, null, null, null, null, null },
        { null, null, null, null, null, null, null, null, null, null, null, null, {T.startStartTag.id, N.Name.id, N.ElementA.id}, null, null },
        { null, null, null, null, null, null, null, null, null, {T.endTag.id, N.ElementT.id}, null, {T.endEmptyTag.id}, null, null, null },
        { {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, {N.Word.id, N.Words.id, N.Endtag.id}, null, null, {N.Endtag.id}, null, {N.Element.id, N.Endtag.id}, null, null },
        { null, null, null, null, null, null, null, null, null, null, {T.startEndTag.id, N.Name.id, T.endTag.id}, null, null, null, null },
        { {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, {N.Word.id, N.Words.id}, null, null, {}, null, null, null, null },
        { {T.underscore.id, N.NamecharT.id}, null, null, {T.colon.id, N.NamecharT.id}, null, null, null, {T.letter.id, N.NamecharT.id}, null, null, null, null, null, null, null },
        { {N.Namechar.id, N.NamecharT.id}, {N.Namechar.id, N.NamecharT.id}, null, {N.Namechar.id, N.NamecharT.id}, null, {N.Namechar.id, N.NamecharT.id}, {N.Namechar.id, N.NamecharT.id}, {N.Namechar.id, N.NamecharT.id}, null, {}, null, {}, null, null, null },
        { {T.underscore.id}, {T.dash.id}, null, {T.colon.id}, null, {T.dot.id}, {T.digit.id}, {T.letter.id}, null, null, null, null, null, null, null },
        { null, null, null, null, null, null, {T.digit.id, N.NumberT.id}, null, null, null, null, null, null, null, null },
        { null, null, null, null, null, {}, {N.Number.id}, null, null, null, null, null, null, {}, null },
        { {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, {N.Char.id, N.WordT.id, T.space.id}, null, null, null, null, null, null, null },
        { {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {N.Char.id, N.WordT.id}, {}, null, null, null, null, null, null },
        { {T.underscore.id}, {T.dash.id}, {T.semicolon.id}, {T.colon.id}, {T.comma.id}, {T.dot.id}, {T.digit.id}, {T.letter.id}, null, null, null, null, null, null, null }
    };

    public SyntaxAnalyzer(String input, MainFrame mainFrame)
    {
        text = input;
		mainWindow = mainFrame;
		
        map.put("-", T.dash);
        map.put("_", T.underscore);
        map.put(";", T.semicolon);
        map.put(":", T.colon);
        map.put(",", T.comma);
        map.put(".", T.dot);
        map.put(" ", T.space);
        map.put("<", T.startStartTag);
        map.put(">", T.endTag);
        map.put("</", T.startEndTag);
        map.put("/>", T.endEmptyTag);
        map.put("<?xml version=", T.startXmlDecl);
        map.put("?>", T.endXmlDecl);
        map.put("letter", T.letter);
        map.put("digit", T.digit);
    }

    public boolean analyze()
    {
        boolean error = false;
        Stack<Integer> stack = new Stack<>();
        LexicalAnalyzer scanner = new LexicalAnalyzer(text);
        
        stack.push(N.Xmldocument.id);
        
        for (String token = scanner.getNextToken(); !token.isEmpty(); token = scanner.getNextToken())
        {
            T terminal = map.get(token);
            if (terminal == null)
            {
				mainWindow.addTextToOutput("Unknown symbol found: " + token + ".");
                error = true;
                continue;
            }
            
            while (true)
            {
                if (stack.isEmpty())
                {
					mainWindow.addTextToOutput("Trying to pop from the stack, but the stack is empty.");
                    error = true;
                    break;
                }
                int itemId = stack.pop();
                N nonTerminal = N.getEnumById(itemId);
                if (nonTerminal != null)
                {
                    int rules[] = table[nonTerminal.ordinal()][terminal.ordinal()];
                    if (rules == null)
                    {
						mainWindow.addTextToOutput("No entry found in parsing table for terminal \"" + terminal +"\" and nonterminal \"" + nonTerminal + "\".");
                        error = true;
                        break;
                    }
					writeStackTrace(scanner.getPrevOffset(), itemId, rules, stack);
                    for (int i = rules.length - 1; i >= 0; i--)
                    {
                        stack.push(rules[i]);
                    }
					
                }
                else
                {
                    T stackTerminal = T.getEnumById(itemId);
                    if (stackTerminal != terminal)
                    {
						mainWindow.addTextToOutput("Terminal from the input \"" + terminal + "\" and terminal on the stack \"" + stackTerminal + "\" are different.");
                        error = true;
                    }
					writeStackTrace(scanner.getPrevOffset(), itemId, null, stack);
                    break;
                }
            }
        }
		writeStackTrace(scanner.getOffset(), -1, null, stack);
		if (!stack.isEmpty())
		{
			mainWindow.addTextToOutput("Parsing finished but stack is not empty.");
		}
        return !error && stack.isEmpty();
    }
	
	private void writeStackTrace(int offset, int lastPop, int rules[], Stack<Integer> stack)
	{
		String record = text.substring(offset) + " ";
		
		if (rules != null)
		{
			record += getSymbolNameById(lastPop) + " ->";
			if (rules.length == 0)
			{
				record += " e";
			}
			for (int i : rules)
			{
				record += " " + getSymbolNameById(i);
			}
		}
		else
		{
			record += "-----";
		}
		
		record += " Stack: " + (lastPop == -1 ? "" : getSymbolNameById(lastPop)) + " ";
		
		Stack<Integer> auxStack = (Stack) stack.clone();
		while (!auxStack.isEmpty())
		{
			record += getSymbolNameById(auxStack.pop()) + " ";
		}
		
		mainWindow.addTextToStackTrace(record);
	}
	
	private String getSymbolNameById(int id)
	{
		N nonterminal = N.getEnumById(id);
		if (nonterminal != null)
		{
			return nonterminal.toString();
		}
		else
		{
			return T.getEnumById(id).toString();
		}
	}
}
